namespace KinonekoSoftware.CodariaDB.Documents.Timelines
{
    partial class TimelineSystem
    {

        //-------------------------------------------------------------
        //
        //         Readonly  Fields
        //
        //-------------------------------------------------------------
        private readonly Dictionary<string, StoryPointNode> _mapOfStoryPoint;

        //-------------------------------------------------------------
        //
        //          Fields
        //
        //-------------------------------------------------------------

        private StoryPointNode _RootOfStoryPoint;
        private int            _CountOfStoryPoint;
        private int            _VersionOfStoryPoint;

        //-------------------------------------------------------------
        //
        //          Private Methods
        //
        //-------------------------------------------------------------
        private void ClearStoryPointStates()
        {

            _RootOfStoryPoint?.Children.Clear();
            _RootOfStoryPoint = null;
            _RootOfStoryPoint = new StoryPointNode();

            _mapOfStoryPoint.Clear();

            StoryPoint      = null;
            StoryPointLayer = null;
            StoryPointCollection.Clear();
            StoryPointAncestorList.Clear();
            StoryPointLayerList.Clear();

            _VersionOfStoryPoint++;
            _CountOfStoryPoint = 0;
        }
        
        
        //-------------------------------------------------------------
        //
        //          SubTree Methods
        //
        //-------------------------------------------------------------
        
        private IEnumerable<StoryPointNode> FindAncestor(StoryPoint point)
        {
            var node = _mapOfStoryPoint[point.Id];
            
            return FindAncestor(node);
        }

        private static IEnumerable<StoryPointNode> FindAncestor(StoryPointNode node)
        {
            var array = new List<StoryPointNode>(8);

            while (node.Parent is not null)
            {
                array.Add(node);
                node = node.Parent;
            }

            return array;
        }

        private IEnumerable<StoryPointNode> GetSubTree(StoryPoint point)
        {
            if (point is null)
            {
                return Array.Empty<StoryPointNode>();
            }

            var node = _mapOfStoryPoint[point.Id];
            
            if (node is null)
            {
                return Array.Empty<StoryPointNode>();
            }

            return GetSubTree(node);
        }

        private IEnumerable<StoryPointNode> GetSubTree(StoryPointNode point)
        {
            var queue = new Queue<StoryPointNode>();
            var list  = new List<StoryPointNode>(32);

            queue.Enqueue(point);

            while (queue.Count > 0)
            {
                var item = queue.Dequeue();

                if (item.Children.Count > 0)
                {
                    foreach (var child in item.Children)
                    {
                        list.Add(child);
                        queue.Enqueue(child);
                    }
                }
            }

            return list;
        }
        
        //-------------------------------------------------------------
        //
        //          Builder Factory Methods
        //
        //-------------------------------------------------------------
        private StoryPoint BuildAttached(string id) => _system.GetAttached(id);

        //-------------------------------------------------------------
        //
        //          Rebuild
        //
        //-------------------------------------------------------------
        private void PerformStoryPointCollection(IEnumerable<StoryPointNode> points)
        {
            StoryPoint = null;

            StoryPointCollection.AddMany(points.Select(x => x.Value), true);
            StoryPointCollection.Sort();
        }

        private void PerformStoryPointLayer()
        {

            //
            //
            var parent = StoryPointLayer is null ? _RootOfStoryPoint : _mapOfStoryPoint[StoryPointLayer.Id];

            //
            //
            PerformStoryPointCollection(parent.Children);

            if (StoryPointLayer is not null)
            {
                StoryPointAncestorList.AddMany(FindAncestor(StoryPointLayer).Select(x => x.Value), true);
            }
        }

        public void RebuildDetachStoryPoint()
        {
            //
            // 获得所有Detached StoryPoint
            DetachedStoryPointCollection.AddMany(_system.GetDetached(), true);
        }

        public void RebuildStoryPoint(Plot plot)
        {
            if (plot is null)
            {
                return;
            }

            //
            //
            ClearStoryPointStates();

            //
            // 第一遍遍历，旨在将所有节点都添加到字典当中。
            var points = plot.Points.Select(BuildAttached);
            var root   = new List<StoryPointNode>(8);

            //
            // 第二遍遍历，恢复树形结构
            foreach (var point in points)
            {
                var node = new StoryPointNode(point);
                _mapOfStoryPoint.Add(point.Id, node);

                if (string.IsNullOrEmpty(point.ParentID))
                {
                    root.Add(node);
                    _RootOfStoryPoint.AddChildAndSetParent(node);
                    _VersionOfEpoch++;
                }

                _CountOfStoryPoint++;
            }

            foreach (var point in _mapOfStoryPoint.Values.Where(x => !string.IsNullOrEmpty(x.ParentID)))
            {

                if (_mapOfStoryPoint.TryGetValue(point.ParentID, out var parent))
                {
                    parent.AddChildAndSetParent(point);
                }
                else
                {
                    point.ParentID = null;
                    _RootOfStoryPoint.AddChildAndSetParent(point);

                    _system.Update(point.Value);
                }

                _VersionOfEpoch++;
            }

            //
            // 第三步，呈现UI
            PerformStoryPointCollection(root);
        }
        
        
        //-------------------------------------------------------------
        //
        //          Attach / Detach Methods
        //
        //-------------------------------------------------------------


        public void Attach(StoryPoint point)
        {
            if (point is null)
            {
                return;
            }

            if (_mapOfStoryPoint.ContainsKey(point.Id))
            {
                return;
            }

            var parent = StoryPointLayer is null ? _RootOfStoryPoint : _mapOfStoryPoint[StoryPointLayer.Id];
            var node   = new StoryPointNode(point);

            //
            //
            parent.AddChildAndSetParent(node);
            node.Index = parent.Children.Count;

            //
            //
            _mapOfStoryPoint.TryAdd(point.Id, node);
            _system.AddAttached(point);

            //
            //
            StoryPointCollection.Add(point);
            DetachedStoryPointCollection.Remove(point);
            
            _VersionOfStoryPoint++;
            _CountOfStoryPoint++;
        }

        public void Attach(Plot plot)
        {
            if (plot is null)
            {
                return;
            }

            var points   = plot.Points.Select(BuildAttached);
            var ancestor = StoryPointLayer is null ? _RootOfStoryPoint : _mapOfStoryPoint[StoryPointLayer.Id];
            var root     = new List<StoryPointNode>(8);
            var map      = new Dictionary<string, StoryPointNode>();


            //
            // 第一遍遍历，旨在将所有节点都添加到字典当中。
            foreach (var point in points)
            {
                var node = new StoryPointNode(point);

                if (_mapOfStoryPoint.ContainsKey(point.Id))
                {
                    continue;
                }

                //
                //
                map.Add(point.Id, node);

                //
                //
                if (string.IsNullOrEmpty(point.ParentID))
                {
                    root.Add(node);
                    _VersionOfEpoch++;
                }

                _CountOfStoryPoint++;
            }

            //
            // 第二遍遍历，恢复树形结构
            foreach (var point in map.Values.Where(x => !string.IsNullOrEmpty(x.ParentID)))
            {
                if (map.TryGetValue(point.ParentID, out var parent))
                {
                    parent.AddChildAndSetParent(parent);
                    _VersionOfEpoch++;
                }

            }

            //
            // 将Point添加到父级中。
            foreach (var child in root)
            {
                ancestor.AddChildAndSetParent(child);
                child.Index = ancestor.Children.Count;
                
                //
                //
                _system.Update(child.Value);
            }
            
        }
        
        public bool IsStoryPointHasChildren(StoryPoint point)
        {
            if (point is null)
            {
                return false;
            }

            var node = _mapOfStoryPoint[point.Id];

            if (node is null)
            {
                return false;
            }

            return node.Children.Count > 0;
        }

        public StoryPoint DetachAsStoryPoint(StoryPoint point)
        {
            if (point is null)
            {
                return null;
            }

            var node   = _mapOfStoryPoint[point.Id];
            var parent = node.Parent;

            //
            // 在InMemTree中移除节点。
            parent.Children.Remove(node);
            
            //
            //
            Plot.Points.Remove(point.Id);

            //
            // Remove and update
            _mapOfStoryPoint.Remove(point.Id);
            _system.AddDetached(point);
            _system.RemoveAttached(point);

            //
            // remove ui
            StoryPointCollection.Remove(point);
            DetachedStoryPointCollection.Add(point);

            //
            //
            _VersionOfStoryPoint++;
            _CountOfStoryPoint--;
            return point;
        }

        private static Plot CreatePlot(StoryPoint point)
        {
            return new Plot
            {
                Id = ID.GetShortID(), 
                Name = point.Name,
                Intro = point.Intro,
                Points = new List<string>(8),
            };
        }

        public Plot DetachAsPlot(StoryPoint point)
        {
            if (point is null)
            {
                return null;
            }

            var oldPlot = Plot;
            var newPlot = CreatePlot(point);
            var plotMap = Plot.Points.ToHashSet();
            var node   = _mapOfStoryPoint[point.Id];
            var parent = node.Parent;
            
            //
            //
            parent.Children.Remove(node);
            
            //
            //
            newPlot.Points.Add(point.Id);
            plotMap.Remove(point.Id);
            
            //
            //
            var subTree = GetSubTree(point);

            //
            //
            foreach (var node2 in subTree)
            {
                //
                // remove map
                _mapOfStoryPoint.Remove(node2.Id);
                plotMap.Remove(node2.Id);
                
                //
                // add plot 
                newPlot.Points.Add(node2.Id);
                
                //
                //
                _VersionOfStoryPoint++;
                _CountOfStoryPoint--;
            }
            
            
            //
            //
            PlotCollection.Add(newPlot);
            oldPlot.Points.AddMany(plotMap, true);
            StoryPointCollection.Remove(point);
            
            _system.Add(newPlot);
            _system.Update(oldPlot);
            
            
            for (var i = 0; i < parent.Children.Count; i++)
            {
                var item = parent.Children[i];

                if (item.Index != i)
                {
                    item.Index = i;
                    _system.UpdateAttached(item.Value);
                }
            }
            
            //
            //
            _VersionOfStoryPoint++;
            _CountOfStoryPoint--;
            
            
            //
            //
            return newPlot;
        }
        
        //-------------------------------------------------------------
        //
        //          Public Methods
        //
        //-------------------------------------------------------------

        private StoryPointNode AddStoryPointToBackend(StoryPoint point)
        {
            StoryPointNode parent;
            int            index;

            if (StoryPointLayer is null)
            {
                parent = _RootOfStoryPoint;
                index  = parent.Children.Count;

                point.ParentID = null;
                point.Index    = index;
            }
            else
            {
                if (!_mapOfStoryPoint.TryGetValue(StoryPointLayer.Id, out parent))
                {
                    throw new InvalidDataException("SelectedStoryPoint属性虽然存在值，但是这个值不在_mapOfStoryPoint字典当中，值没有被Tracking！");
                }

                index = parent.Children.Count;

                point.ParentID = parent.Id;
                point.Index    = index;

            }
            
            //
            // add to backend
            _system.AddAttached(point);
            return new StoryPointNode(parent, point);
        }

        public void Add(StoryPoint point)
        {
            if (point is null)
            {
                return;
            }

            var node = AddStoryPointToBackend(point);

            //
            //
            _mapOfStoryPoint.TryAdd(node.Id, node);

            //
            //
            Plot.Points.Add(node.Id);

            //
            //
            StoryPointCollection.Add(point);

            //
            //
            _VersionOfStoryPoint++;
            _CountOfStoryPoint++;
        }

        public void ShiftUp(StoryPoint point)
        {
            if (point is null)
            {
                return;
            }

            var idxOfStoryPoint = StoryPointCollection.IndexOf(point);

            //
            // 允许上移的条件，指的是该元素所处的索引必须是[1]或者更高才允许上移
            if (idxOfStoryPoint < 1)
            {
                return;
            }
            
            var upItem   = StoryPointCollection[idxOfStoryPoint - 1];
            var downItem = StoryPointCollection[idxOfStoryPoint];

            
            //
            // 交换值。
            (upItem.Index, downItem.Index) = (downItem.Index, upItem.Index);
            
            //
            // 更新UI
            StoryPointCollection.Move(idxOfStoryPoint - 1, idxOfStoryPoint);

            //
            //
            _system.UpdateAttached(upItem);
            _system.UpdateAttached(downItem);
        }

        public void ShiftDown(StoryPoint point)
        {
            if (point is null)
            {
                return;
            }

            var idxOfStoryPoint = StoryPointCollection.IndexOf(point);

            //
            // 允许上移的条件，指的是该元素所处的索引必须是[1]或者更高才允许上移
            if (idxOfStoryPoint < 1)
            {
                return;
            }
            
            var upItem   = StoryPointCollection[idxOfStoryPoint];
            var downItem = StoryPointCollection[idxOfStoryPoint + 1];

            
            //
            // 交换值。
            (upItem.Index, downItem.Index) = (downItem.Index, upItem.Index);
            
            //
            // 更新UI
            StoryPointCollection.Move(idxOfStoryPoint, idxOfStoryPoint + 1);

            //
            //
            _system.UpdateAttached(upItem);
            _system.UpdateAttached(downItem);
        }

        private StoryPointNode RemoveImpl(StoryPoint point)
        {
            var node = _mapOfStoryPoint[point.Id];

            if (node is null)
            {
                return null;
            }

            //
            //
            _mapOfStoryPoint.Remove(point.Id);
            Plot.Points.Remove(point.Id);
            
            _system.RemoveAttached(point);
            
            //
            //
            _VersionOfStoryPoint++;
            _CountOfStoryPoint--;
            

            return node;
        }
        
        public void Remove(StoryPoint point)
        {
            if (point is null)
            {
                return;
            }

            //
            //
            var node   = RemoveImpl(point);
            var parent = node.Parent;
            
            //
            //
            _system.Update(Plot);
            
            //
            //
            StoryPointCollection.Remove(point);

            //
            // 维护顺序。
            if (parent is not null)
            {
                for (var i = 0; i < parent.Children.Count; i++)
                {
                    var item = parent.Children[i];

                    if (item.Index != i)
                    {
                        item.Index = i;
                        _system.UpdateAttached(item.Value);
                    }
                }
            }
        }

        public void RemoveSubTree(StoryPoint point)
        {
            if (point is null)
            {
                return;
            }

            var node    = RemoveImpl(point);
            var tree    = GetSubTree(node);
            var parent  = node.Parent;
            var plotMap = Plot.Points.ToHashSet();

            //
            //
            parent.Children.Remove(node);
            
            //
            //
            _VersionOfStoryPoint++;
            _CountOfStoryPoint--;
            
            //
            //
            foreach (var node2 in tree)
            {
                plotMap.Remove(node2.Id);
                _mapOfStoryPoint.Remove(node2.Id);
                
                //
                //
                _VersionOfStoryPoint++;
                _CountOfStoryPoint--;
            }

            //
            // 维护顺序。
            for (var i = 0; i < parent.Children.Count; i++)
            {
                var item = parent.Children[i];

                if (item.Index != i)
                {
                    item.Index = i;
                    _system.UpdateAttached(item.Value);
                }
            }
            
            //
            //
            StoryPointCollection.Remove(point);
            Plot.Points.AddMany(plotMap, true);
            
            //
            //
            _system.Update(Plot);
        }

        public Branch CreateBranch()
        {
            return null;
        }

        //-------------------------------------------------------------
        //
        //          Layer Methods
        //
        //-------------------------------------------------------------
        public StoryPoint PopStoryPointLayer()
        {
            if (StoryPointLayerList.Count < 1)
            {
                ResetStoryPointLayer();
                return null;
            }

            StoryPointLayerList.RemoveAt(StoryPointLayerList.Count - 1);
            
            if(StoryPointLayerList.Count == 0)
            {
               
                ResetStoryPointLayer();
                return null; 
            } 
            
            StoryPointLayer = StoryPointLayerList[^1];
            
            PerformStoryPointLayer();

            return StoryPointLayer;
        }

        public void PushStoryPointLayer(StoryPoint point)
        {
            if (point is null)
            {
                StoryPointLayer = null;
                StoryPointLayerList.Clear();
            }
            else
            {
                StoryPointLayer = point;
                StoryPointLayerList.Add(point);
            }
            
            PerformStoryPointLayer();
        }

        public void ResetStoryPointLayer()
        {
            StoryPointLayer = null;
            StoryPointLayerList.Clear();
            PerformStoryPointLayer();
        }


        //-------------------------------------------------------------
        //
        //          Properties
        //
        //-------------------------------------------------------------
        public int CountOfStoryPoint   => _CountOfStoryPoint;
        public int VersionOfStoryPoint => _VersionOfStoryPoint;

        //-------------------------------------------------------------
        //
        //          UI
        //
        //-------------------------------------------------------------
        public StoryPoint StoryPoint      { get; set; }
        public StoryPoint StoryPointLayer { get; set; }

        public ObservableCollection<StoryPoint> DetachedStoryPointCollection { get; }
        public ObservableList<StoryPoint>       StoryPointCollection         { get; }
        public ObservableCollection<StoryPoint> StoryPointLayerList          { get; }
        public ObservableCollection<StoryPoint> StoryPointAncestorList       { get; }


        //-------------------------------------------------------------
        //
        //          Nested Classes
        //
        //-------------------------------------------------------------

        public sealed class StoryPointNode
        {
            private StoryPointNode _parent;
            private byte           _height;

            internal StoryPointNode()
            {
                _parent  = null;
                _height  = 1;
                Children = new List<StoryPointNode>(8);
            }

            internal StoryPointNode(StoryPointNode parent, StoryPoint value)
            {
                _parent  = parent;
                _height  = (byte)(parent._height + 1);
                Value    = value;
                Children = new List<StoryPointNode>(8);

                //
                //
                parent.Children.Add(this);
            }

            internal StoryPointNode(StoryPoint value)
            {
                Value    = value;
                Children = new List<StoryPointNode>(8);
            }

            internal void SetParent(StoryPointNode parent)
            {
                if (Parent is not null)
                {
                    return;
                }

                _parent = parent;
                _height = (byte)(parent._height + 1);
            }

            internal void AddChildAndSetParent(StoryPointNode child)
            {
                child.SetParent(this);
                Children.Add(child);
            }

            public string               Id       => Value.Id;
            public StoryPointNode       Parent   => _parent;
            public byte                 Height   => _height;
            public StoryPoint           Value    { get; }
            public List<StoryPointNode> Children { get; }

            public string ParentID
            {
                get => Value.ParentID;
                set => Value.ParentID = value;
            }

            public int Index
            {
                get => Value.Index;
                set => Value.Index = value;
            }
        }
    }
}